require_relative 'genetic_algorithm.rb'

module Kernel
  def suppress_warnings()
    original_verbosity = $VERBOSE
    $VERBOSE = nil
    result = yield
    $VERBOSE = original_verbosity
    return result
  end
end

module Genetics
  class CalcGenome < Genome.sized(2**6)
    GENES = (0..9).map{|n| ['','+','-','*','/','%','#'].map{|x| "#{n}#{x}"}}.flatten
    GENE_SIZE = GENES.size.to_s(2).size
    
    @@goal = 100
    
    def self.goal=(n)
      @@goal = n
    end
    
    def self.goal()
      @@goal
    end
    
    attr_reader :backtrace, :last_eval
    
    def result()
      out = ''
      i = 0
      until i >= self.size
        gene = 0
        GENE_SIZE.times do |j|
          gene += self.info[i]*(2**j)
          i += 1
        end
        if gene < GENES.size
          out << "#{GENES[gene]}"
        else
          out << ''
        end
      end
      return out
    end
    
    def fitness()
      @backtrace = nil
      score = 0
      r = ''
      begin
        r = "#{self.result}"
        q = nil
        suppress_warnings { q = ( eval("#{r}") ).to_i }
        @last_eval = q
        score = (100.0 * @@goal / (q > @@goal ? q-@@goal : @@goal-q)).to_i
      rescue Exception => e
        @backtrace = e.backtrace
        score -= r.size
      end
      puts "Non-Numeric class: #{score.class}" unless score.is_a? Numeric
      return score
    end
  end
  
  class GeneticAlgorithm
    def genome_s(b=fittest(1).first, i=@i_step)
      "#{i.to_s.rjust(3,'0')}: #{b.result.ljust(20)} => #{b.last_eval.to_s.rjust(5)}, #{b.last_fitness(@i_step)}"
    end
  end
end

if $0 == __FILE__
  include Genetics
  g = GeneticAlgorithm.new(100,30,CalcGenome)
  CalcGenome.goal = 0
  g.run(200) { |i| puts "#{g.genome_s}"; CalcGenome.goal = i }
  puts "\nFittest genome is: \n#{g.best_s}"
end